#include "stdafx.h"
#include "game_engine.h"
#include "dot_scene.h"

namespace rpg {

	GameEngine::GameEngine(void) {
		/* set all members to NULL */
		mOgreRoot = NULL;
		mOgreWindow = NULL;
		mOgreScene = NULL;
		mCamera = NULL;
		mViewport = NULL;
		mScreenWidth = mScreenHeight = 0;
		mMouseX = mMouseY = 0;

		mInputManager = NULL;
		mKeyboard = NULL;
		mMouse = NULL;

		mGuiRenderer = NULL;
		mGuiSystem = NULL;

		mQuitFlag = false;
		mKeyboardNextRepeat = 0.0f;
		mMouseSensivity = 0.1f;
		mCrossNode = NULL;
		mCrossPosition = Ogre::Vector3::ZERO;
	}

	GameEngine::~GameEngine(void) {
		if(mInputManager != NULL) {
			if(mKeyboard != NULL)
				mInputManager->destroyInputObject(mKeyboard);

			if(mMouse != NULL)
				mInputManager->destroyInputObject(mMouse);

			OIS::InputManager::destroyInputSystem(mInputManager);
		}

		if(mGuiSystem != NULL)
			delete mGuiSystem;

		if(mGuiRenderer != NULL)
			delete mGuiRenderer;

		if(mOgreRoot != NULL)
			delete mOgreRoot;
	}

	void GameEngine::initializeGraphics(void) {
		loadOgreRuntime();
		loadOgreResourcesPath();
		loadOgreRenderSystem();
		loadOgreGraphicsResources();
		loadOgreScene();
		loadCEGUI();
		loadGUIScheme();

		loadScene();
	}

	void GameEngine::initializeInput(void) {
		loadInputSystem();
	}

	void GameEngine::runGameLoop(void) {
		mOgreRoot->startRendering();
	}

	void GameEngine::quit(void) {
		mQuitFlag = true;
	}

	void GameEngine::loadScene(void) {
		loadSceneEnvironment();
		loadSceneCamera();
		loadSceneGeometry();
	}

	void GameEngine::loadSceneEnvironment(void) {
		mOgreScene->setShadowTechnique(Ogre::SHADOWTYPE_STENCIL_MODULATIVE);

		Ogre::Light *sunLight = mOgreScene->createLight("SunLight");
		sunLight->setType(Ogre::Light::LT_DIRECTIONAL);
		sunLight->setDirection(Ogre::Vector3(-3.0f, -2.0f, 1.0f));
	}

	void GameEngine::loadSceneCamera(void) {

	}

	void GameEngine::loadSceneGeometry(void) {
		DotScene dotScene(mOgreScene);
		Ogre::SceneNode *gameLevel0 = mOgreScene->getRootSceneNode()->createChildSceneNode("Game/Level0");

		dotScene.load("GroundPlane.scene", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, gameLevel0);

		Ogre::Entity *treeEntity = mOgreScene->createEntity("Tree0", "Foliage01.mesh");
		Ogre::SceneNode *tree = gameLevel0->createChildSceneNode();
		tree->attachObject(treeEntity);
		tree->scale(Ogre::Vector3::UNIT_SCALE * 0.1f);
		tree->setPosition(Ogre::Vector3::NEGATIVE_UNIT_Z * 2.0f);

		treeEntity = mOgreScene->createEntity("Tree1", "Foliage01.mesh");
		tree = gameLevel0->createChildSceneNode();
		tree->attachObject(treeEntity);
		tree->scale(Ogre::Vector3::UNIT_SCALE * 0.1f);
		tree->setPosition(Ogre::Vector3::UNIT_X * 4.0f + Ogre::Vector3::NEGATIVE_UNIT_Z * 2.0f);

		gameLevel0->scale(Ogre::Vector3::UNIT_SCALE * 10.0f);

		Ogre::Entity *sceneEntity = mOgreScene->createEntity("Robot0", "robot.mesh");
		Ogre::SceneNode *sceneNode = mOgreScene->getRootSceneNode()->createChildSceneNode("Game/Robot");
		sceneNode->attachObject(sceneEntity);

		ActorDesc actorDesc;

		actorDesc.levelObjectDesc.name = "Game/Robo0";
		actorDesc.levelObjectDesc.position = Ogre::Vector3::ZERO;
		actorDesc.levelObjectDesc.turnAngle = 0.0f;
		actorDesc.ogreSceneNode = sceneNode;
		actorDesc.idleAnimationName = ACTOR_DEFAULT_IDLE_ANIMATION_NAME;
		actorDesc.walkAnimationName = ACTOR_DEFAULT_WALK_ANIMATION_NAME;
		actorDesc.maxMoveSpeed = 5.5f;

		Actor *actor = mGameLevel.addNewActor(actorDesc);

		sceneNode->scale(Ogre::Vector3::UNIT_SCALE * 0.1f);

		sceneEntity = mOgreScene->createEntity("Cross", "cross.mesh");
		mCrossNode = mOgreScene->getRootSceneNode()->createChildSceneNode("Game/Cross0");
		mCrossNode->attachObject(sceneEntity);
		mCrossNode->setVisible(true);

		GameCameraDesc gameCameraDesc;

		gameCameraDesc.levelObjectDesc.name = "Game/Camera0";
		gameCameraDesc.levelObjectDesc.position = Ogre::Vector3::ZERO;
		gameCameraDesc.levelObjectDesc.turnAngle = 0.0f;
		gameCameraDesc.ogreCamera = mCamera;
		gameCameraDesc.targetPoint = Ogre::Vector3::ZERO;
		gameCameraDesc.targetObject = actor;
		gameCameraDesc.cameraView = CAMERA_VIEW_FROM_SOUTH_TO_NORTH;
		gameCameraDesc.distanceToTarget = 40.0f;

		mGameLevel.addNewCamera(gameCameraDesc);
		mGameLevel.setActiveCamera("Game/Camera0");
	}

	void GameEngine::loadOgreRuntime(void) {
		if(mOgreRoot == NULL) {
			mOgreRoot = new Ogre::Root(OGRE_CFG_PLUGINS_PATH, OGRE_CFG_PATH, OGRE_LOG_PATH);
			mOgreRoot->addFrameListener(this);
		}
	}

	void GameEngine::loadOgreResourcesPath(void) {
		Ogre::String secName, typeName, archName;
		Ogre::ConfigFile cf;

		cf.load("./../data/config/resources.cfg");

		Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();

		while (seci.hasMoreElements()) {
			secName = seci.peekNextKey();
			Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
			Ogre::ConfigFile::SettingsMultiMap::iterator i;

			for(i = settings->begin(); i != settings->end(); ++i) {
				typeName = i->first;
				archName = i->second;

				Ogre::ResourceGroupManager::getSingleton().addResourceLocation(archName, typeName, secName);
			}
		}
	}

	void GameEngine::loadOgreRenderSystem(void) {
		/* If there is no config file show user configuration window else load previous configuration*/
		if(!mOgreRoot->restoreConfig() && !mOgreRoot->showConfigDialog())
			throw Ogre::Exception(52, "User canceled the config dialog!", "Application::loadOgreRenderSystem()");

		/* Show Ogre window */
		mOgreWindow = mOgreRoot->initialise(true, "Lords of the sky : WIP");
	}

	void GameEngine::loadOgreGraphicsResources(void) {
		Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
	}

	void GameEngine::loadOgreScene(void) {
		mOgreScene = mOgreRoot->createSceneManager(Ogre::ST_GENERIC, "Game/MainScene");
		mCamera = mOgreScene->createCamera("Game/MainCamera");
		mViewport = mOgreWindow->addViewport(mCamera);

		mViewport->setBackgroundColour(Ogre::ColourValue(0.5f, 0.5f, 0.5f, 1.0f));

		mCamera->setAspectRatio(float(mViewport->getActualWidth()) / float(mViewport->getActualHeight()));
		mCamera->setFOVy(Ogre::Degree(60.0f));
		mCamera->setNearClipDistance(1.0f);
		mCamera->setFarClipDistance(1000.0f);
		mCamera->setPosition(20.0f, 30.0f, 0.0f);
		mCamera->lookAt(0.0f, 0.0f, 0.0f);

		mScreenWidth = mViewport->getActualWidth();
		mScreenHeight = mViewport->getActualHeight();

		mMouseX = mScreenWidth / 2;
		mMouseY = mScreenHeight / 2;
	}

	void GameEngine::loadInputSystem(void) {
		size_t windowHandle;

		mOgreWindow->getCustomAttribute(Ogre::String("WINDOW"), &windowHandle);

		mInputManager = OIS::InputManager::createInputSystem(windowHandle);
		mKeyboard = static_cast<OIS::Keyboard *>(mInputManager->createInputObject(OIS::OISKeyboard, false));
		mMouse = static_cast<OIS::Mouse *>(mInputManager->createInputObject(OIS::OISMouse, false));

		mMouse->capture();
		mOldMouseState = mMouse->getMouseState();
	}

	void GameEngine::captureKeyboard(void) {
		mKeyboard->capture();
	}

	void GameEngine::captureMouse(void) {
		mMouse->capture();
	}

	void GameEngine::loadCEGUI(void) {
		mGuiRenderer = new CEGUI::OgreCEGUIRenderer(mOgreWindow, Ogre::RENDER_QUEUE_OVERLAY, false, 3000, mOgreScene);
		mGuiSystem = new CEGUI::System(mGuiRenderer);

		mGuiWindowManager = CEGUI::WindowManager::getSingletonPtr();
	}

	void GameEngine::loadGUIScheme(void) {
		CEGUI::SchemeManager::getSingleton().loadScheme((CEGUI::utf8 *)"AquaLookSkin.scheme");
		mGuiSystem->setDefaultMouseCursor((CEGUI::utf8 *)"AquaLook", (CEGUI::utf8 *)"MouseArrow");
		mGuiSystem->setDefaultFont((CEGUI::utf8 *)"BlueHighway-12");
		CEGUI::MouseCursor::getSingleton().setImage(mGuiSystem->getDefaultMouseCursor());

		mGuiSheet = mGuiWindowManager->createWindow("DefaultGUISheet", "CEGUIDemo/Sheet");

		CEGUI::Window *quit = mGuiWindowManager->createWindow("AquaLook/Button", "CEGUIDemo/QuitButton");
		quit->setText("Quit");
		quit->setPosition(CEGUI::UVector2(CEGUI::UDim(0.85, 0), CEGUI::UDim(0.95f, 0)));
		quit->setSize(CEGUI::UVector2(CEGUI::UDim(0.15, 0), CEGUI::UDim(0.03, 0)));

		mGuiSheet->addChildWindow(quit);

		mGuiSystem->setGUISheet(mGuiSheet);

		quit->subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber(&GameEngine::ceguiEvent, this));
	}

	bool GameEngine::ceguiEvent(const CEGUI::EventArgs &e) {
		quit();
		return true;
	}

}